#define SERVER_IP_ADDR "0.0.0.0"  // 服务器IP地址
#define SERVER_PORT 8080          // 服务器端口号
#define BACKLOG 10
#define BUF_SIZE 8192
#define OK 1
#define ERROR 0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>

const char* Server_name = "Server: Web Server 1.0 - BooLo\r\n";

int Server_Socket_Init(int port);
int Handle_Request_Message(char* message, int Socket);
int Judge_URI(char* URI, int Socket);
int Send_Ifon(int Socket, const char* sendbuf, int Length);
int Error_Request_Method(int Socket);
int Inquire_File(char* URI);
int File_not_Inquire(int Socket);
int Send_File(char* URI, int Socket);
const char* Judge_Method(char* method, int Socket);
const char* Judge_File_Type(char* URI, const char* content_type);
const char* Get_Data(const char* cur_time);
const char* Post_Value(char* message);
int Logo();

// 创建套接字函数
int Server_Socket_Init(int port){
    int ServerSock;
    struct sockaddr_in ServerAddr;

    // 创建套接字
    ServerSock = socket(AF_INET, SOCK_STREAM, 0);
    if(ServerSock < 0){
        perror("Failed to create socket!");
        exit(1);
    }

    // 配置服务器IP、端口信息
    memset(&ServerAddr, 0, sizeof(ServerAddr));
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_port = htons(port);
    ServerAddr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR);

    // 绑定
    if(bind(ServerSock, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr)) < 0){
        perror("Failed to bind stream socket!");
        exit(1);
    }

    return ServerSock;
}

// 处理浏览器发送的数据

int Handle_Request_Message(char* message, int Socket){

    int rval = 0;
    char Method[BUF_SIZE];
    char URI[BUF_SIZE];
    char Version[BUF_SIZE];

    if(sscanf(message, "%s %s %s", Method, URI, Version) != 3){
        printf("Request line error!\n");
        return ERROR;
    }

    printf("$$$ Method: %s, URI: %s, Version %s \n",Method, URI, Version);

    if(Judge_Method(Method, Socket) == ERROR){
        return ERROR;
    }else if(strcmp(Judge_Method(Method, Socket), "POST") == 0){
        Post_Value(message);
    }

    if(strcmp(URI, "/") == 0){
        strcpy(URI, "/ndex.html");
    }

    if(Judge_URI(URI, Socket) == ERROR){
        return ERROR;
    } else {
        rval = Send_File(URI, Socket);  // 发送数据html文件或者其他
    }

    if(rval == OK){
        printf("The process is successfully finished!\n");
    }

    return OK;
}

// 查看传入的请求类型  GET是向服务器获取数据，POST是向服务器提交数据
const char* Judge_Method(char* method, int Socket){

    if(strcmp(method, "GET") == 0){
        return "GET";
    }else if(strcmp(method, "POST") == 0){
        return "POST";
    }else{
        Error_Request_Method(Socket);
        return ERROR;
    }

}

int Judge_URI(char* URI, int Socket){

    if (Inquire_File(URI) == ERROR){
        File_not_Inquire(Socket);
        return ERROR;
    } else {
        return OK;
    }

}

// 等待数据被成功发送
int Send_Ifon(int Socket, const char* sendbuf, int Length){

    int sendtotal = 0, bufleft, rval = 0;

    bufleft = Length;
    while(sendtotal < Length){
        rval = send(Socket, sendbuf + sendtotal, bufleft, 0);
        if(rval < 0){
            break;
        }
        sendtotal += rval;
        bufleft -= rval;
    }

    return rval < 0 ? ERROR : OK;

}

int Error_Request_Method(int Socket){

    const char* Method_err_line = "HTTP/1.1 501 Not Implemented\r\n";
    const char* cur_time = "";
    const char* Method_err_type = "Content-type: text/plain\r\n";
    const char* File_err_length = "Content-Length: 41\r\n";
    const char* Method_err_end = "\r\n";
    const char* Method_err_info = "The request method is not yet completed!\n";

    printf("The request method from client's request message is not yet completed!\n");

    if(Send_Ifon(Socket, Method_err_line, strlen(Method_err_line)) == ERROR){
        printf("Sending method_error_line failed!\n");
        return ERROR;
    }

    if(Send_Ifon(Socket, Server_name, strlen(Server_name)) == ERROR){
        printf("Sending Server_name failed!\n");
        return ERROR;
    }

    cur_time = Get_Data(cur_time);
    Send_Ifon(Socket, "Date: ", 6);
    if(Send_Ifon(Socket, cur_time, strlen(cur_time)) == ERROR){
        printf("Sending cur_time error!\n");
        return ERROR;
    }

    if(Send_Ifon(Socket, Method_err_type, strlen(Method_err_type)) == ERROR){
        printf("Sending method_error_type failed!\n");
        return ERROR;
    }

    if(Send_Ifon(Socket, Method_err_end, strlen(Method_err_end)) == ERROR){
        printf("Sending method_error_end failed!\n");
        return ERROR;
    }

    if(Send_Ifon(Socket, Method_err_info, strlen(Method_err_info)) == ERROR){
        printf("Sending method_error_info failed!\n");
        return ERROR;
    }

    return OK;

}

int Inquire_File(char* URI){

    char cwd[BUF_SIZE];
    if(getcwd(cwd, sizeof(cwd)) == NULL){
        perror("getcwd() error");
        return ERROR;
    }

    char abs_path[BUF_SIZE];
    snprintf(abs_path, sizeof(abs_path), "%s%s", cwd, URI);

    struct stat File_info;
    if(stat(abs_path, &File_info) == -1){
        return ERROR;
    }else{
        return OK;
    }

}

int File_not_Inquire(int Socket) {

        const char* File_err_line = "HTTP/1.1 404 Not Found\r\n";
        const char* cur_time = "";
        const char* File_err_type = "Content-type: text/html\r\n";
        const char* File_err_end = "\r\n";

        FILE* file;
        struct stat file_stat;
        char sendbuf[BUF_SIZE];
        int send_length;

        char cwd[BUF_SIZE];
        if (getcwd(cwd, sizeof(cwd)) == NULL){
            perror("getcwd() error");
            return ERROR;
        }

        char abs_path[BUF_SIZE];
        snprintf(abs_path, sizeof(abs_path), "%s/404.html", cwd);

        file = fopen(abs_path, "rb");
        if(file != NULL){
            fstat(fileno(file), &file_stat);

            if(Send_Ifon(Socket, File_err_line, strlen(File_err_line)) == ERROR){
                printf("Sending file_error_line error!\n");
                fclose(file);
                return ERROR;
            }

            if(Send_Ifon(Socket, Server_name, strlen(Server_name)) == ERROR){
                printf("Sending Server_name failed!\n");
                fclose(file);
                return ERROR;
            }

            cur_time = Get_Data(cur_time);
            Send_Ifon(Socket, "Date: ", 6);
            if(Send_Ifon(Socket, cur_time, strlen(cur_time)) == ERROR){
                printf("Sending cur_time error!\n");
                fclose(file);
                return ERROR;
            }

            if(Send_Ifon(Socket, File_err_type, strlen(File_err_type)) == ERROR){
                printf("Sending file_error_type error!\n");
                fclose(file);
                return ERROR;
            }

            char content_length[BUF_SIZE];
            snprintf(content_length, sizeof(content_length), "Content-Length: %ld\r\n", file_stat.st_size);
            if(Send_Ifon(Socket, content_length, strlen(content_length)) == ERROR){
                printf("Sending file_error_length error!\n");
                fclose(file);
                return ERROR;
            }
            if(Send_Ifon(Socket, File_err_end, strlen(File_err_end)) == ERROR){
                    printf("Sending file_error_end error!\n");
                    fclose(file);
                    return ERROR;
                }

            while((send_length = fread(sendbuf, 1, BUF_SIZE, file)) > 0){
                if (Send_Ifon(Socket, sendbuf, send_length) == ERROR) {
                    printf("Sending 404.html content error!\n");
                    break;
                }
            }

            fclose(file);
        }else{
            printf("Failed to open 404.html!\n");
            return ERROR;
        }

    return OK;

}

// 构建发送格式发送文件给浏览器函数

/**
 * const char* response = 
    "HTTP/1.1 200 OK\r\n"  // 状态行
    "Date: Wed, 08 Jan 2025 12:00:00 GMT\r\n"  // 日期时间
    "Content-Type: text/html\r\n"  // 内容类型
    "Content-Length: 1234\r\n"  // 内容长度
    "\r\n"  // 响应头结束
    "<html>...</html>";  // 响应体（文件内容）
 */


int Send_File(char* URI, int Socket) {

    char cwd[BUF_SIZE];
    if(getcwd(cwd, sizeof(cwd)) == NULL){
        perror("getcwd() error");
        return ERROR;
    }

    char abs_path[BUF_SIZE];
    snprintf(abs_path, sizeof(abs_path), "%s%s", cwd, URI);  // 路径

    const char* File_ok_line = "HTTP/1.1 200 OK\r\n";   // 成功响应
    const char* cur_time = "";
    const char* File_ok_type = "";
    const char* File_ok_length = "Content-Length: ";
    const char* File_ok_end = "\r\n";

    FILE* file;
    struct stat file_stat;
    char Length[BUF_SIZE];
    char sendbuf[BUF_SIZE];
    int send_length;

    if(Judge_File_Type(abs_path, File_ok_type) == ERROR){
        printf("The request file's type from client's request message is error!\n");
        return ERROR;
    }

    file = fopen(abs_path, "rb");
    if(file != NULL){
        fstat(fileno(file), &file_stat); // 获取文件储存信息
        snprintf(Length, sizeof(Length), "%ld", file_stat.st_size);

        if(Send_Ifon(Socket, File_ok_line, strlen(File_ok_line)) == ERROR){     // 发送接收信息成功头部
            printf("Sending file_ok_line error!\n");
            return ERROR;
        }

        if(Send_Ifon(Socket, Server_name, strlen(Server_name)) == ERROR){   // 发送服务器响应头
            printf("Sending Server_name failed!\n");
            return ERROR;
        }

        cur_time = Get_Data(cur_time);
        Send_Ifon(Socket, "Date: ", 6);
        if(Send_Ifon(Socket, cur_time, strlen(cur_time)) == ERROR){ // 发送时间日期信息
            printf("Sending cur_time error!\n");
            return ERROR;
        }

        File_ok_type = Judge_File_Type(abs_path, File_ok_type);
        if(Send_Ifon(Socket, File_ok_type, strlen(File_ok_type)) == ERROR){ // 发送内容类型
            printf("Sending file_ok_type error!\n");
            return ERROR;
        }

        if(Send_Ifon(Socket, File_ok_length, strlen(File_ok_length)) != ERROR){ // 发送内容长度
            if(Send_Ifon(Socket, Length, strlen(Length)) != ERROR){
                if(Send_Ifon(Socket, "\n", 1) == ERROR){
                    printf("Sending file_ok_length error!\n");
                    return ERROR;
                }
            }
        }

        if(Send_Ifon(Socket, File_ok_end, strlen(File_ok_end)) == ERROR){   // 响应结束
            printf("Sending file_ok_end error!\n");
            return ERROR;
        }
        // 发送响应体
        while(file_stat.st_size > 0){
            if(file_stat.st_size < 1024){
                send_length = fread(sendbuf, 1, file_stat.st_size, file);
                if(Send_Ifon(Socket, sendbuf, send_length) == ERROR){
                    printf("Sending file information error!\n");
                    continue;
                }
                file_stat.st_size = 0;
            }else{
                send_length = fread(sendbuf, 1, 1024, file);
                if(Send_Ifon(Socket, sendbuf, send_length) == ERROR){
                    printf("Sending file information error!\n");
                    continue;
                }
                file_stat.st_size -= 1024;
            }
        }
    }else{
        printf("The file is NULL!\n");
        return ERROR;
    }

    return OK;

}

// 确定传输的文件类型

const char* Judge_File_Type(char* URI, const char* content_type) {
    const char* suffix;

    if ((suffix = strrchr(URI, '.')) != NULL)
        suffix = suffix + 1;

    if (strcmp(suffix, "html") == 0) {
        return "Content-type: text/html\r\n";
    } else if (strcmp(suffix, "jpg") == 0) {
        return "Content-type: image/jpeg\r\n";
    } else if (strcmp(suffix, "png") == 0) {
        return "Content-type: image/png\r\n";
    } else if (strcmp(suffix, "gif") == 0) {
        return "Content-type: image/gif\r\n";
    } else if (strcmp(suffix, "txt") == 0) {
        return "Content-type: text/plain\r\n";
    } else if (strcmp(suffix, "xml") == 0) {
        return "Content-type: text/xml\r\n";
    } else if (strcmp(suffix, "rtf") == 0) {
        return "Content-type: text/rtf\r\n";
    } else if (strcmp(suffix, "js") == 0) {
        return "Content-type: application/javascript\r\n";
    } else if (strcmp(suffix, "css") == 0) {
        return "Content-type: text/css\r\n";
    } else if (strstr(URI, ".mp3") != NULL) {
        return "Content-Type: audio/mpeg\r\n";
    } else {
        return ERROR;
    }
}

const char* Get_Data(const char* cur_time) {
    time_t curtime;
    time(&curtime);
    cur_time = ctime(&curtime);

    return cur_time;
}

const char* Post_Value(char* message) {
    const char* suffix;

    if ((suffix = strrchr(message, '\n')) != NULL)
        suffix = suffix + 1;
    printf("\n\n$$$$-1 Post Value: %s\n\n", suffix);

    return suffix;
}

int Logo() {

    printf("／￣￣￣￣￣￣￣￣￣\n");
    printf("|  程序启动了！\n");
    printf(" \n");
    printf(" ￣￣∨￣￣￣￣￣￣\n");
    printf("  ∧＿∧\n");
    printf(" ( ∧_∧) \n");
    printf(" (  つつヾ\n");
    printf("  | | |\n");
    printf(" (__)_)\n");
    printf("\n");
    printf("___________________________________________________________\n\n");
    return OK;
}

int main(int argc, char* argv[]) {
    // 初始化套接字
    int port = SERVER_PORT;

    if (argc == 2) {
        port = atoi(argv[1]);
    }

    int ServerSock, MessageSock;
    struct sockaddr_in ClientAddr;
    int rval, Length;
    char revbuf[BUF_SIZE];

    Logo();
    printf("Web Server is starting on port %d...\n\n", port);
    ServerSock = Server_Socket_Init(port);
    printf("\n-----------------------------------------------------------\n");

    // 监听和处理请求

    rval = listen(ServerSock, BACKLOG); // 监听
    if (rval < 0) {
        perror("Failed to listen socket!");
        exit(1);
    }
    printf("Listening the socket on port %d...\n", port);
    Length = sizeof(ClientAddr);
    

    while (OK) {

        MessageSock = accept(ServerSock, (struct sockaddr*)&ClientAddr, &Length);   // 创建与客户端通信的套接字
        if (MessageSock < 0) {
            perror("Failed to accept connection from client!");
            exit(1);
        }
        printf("Succeed to accept connection from [%s:%d] !\n\n", inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port));

        memset(revbuf, 0, BUF_SIZE);
        rval = recv(MessageSock, revbuf, BUF_SIZE, 0);
        if (rval <= 0) {
            printf("Failed to receive request message from client!\n");
        } else {
            printf("$$$$$$ recvbuf: %s\n", revbuf);
            rval = Handle_Request_Message(revbuf, MessageSock);
        }

        printf("\n-----------------------------------------------------------\n");
        close(MessageSock);
    }

     
    close(ServerSock);

    return OK;
}